﻿#include "Ast.h"

#include <QDir>
#include <QFileInfo>
#include <QVector>

#include "AstWorker.h"
#include "AstDll.h"
#include "AstStruct.h"
#include "AstFunction.h"
#include "AstSceneGroup.h"
//#include "AstWorkerImpl.h"

#include "OpenSceneFunction.h"
#include "LessFunction.h"
#include "GreaterFunction.h"
#include "PlusFunction.h"

#include "../lib/env.h"
#include "../lib/function.h"
#include "../lib/function_group.h"
#include "../lib/variable.h"
#include "../lib/variable_group.h"
#include "../lib/variant.h"

class Ast::AstPrivate :public QObject
{
public:
	//void				* env;
	struct function_group_s * function_group;
	struct variable_group_s * constant_group;

	OpenSceneFunction	*openscene_function;
	LessFunction		*less_function;
	GreaterFunction		*greater_function;
	PlusFunction		*plus_function;

	AstDllGroup			* astdll_group;
	AstSceneGroup		* astscene_group;
	//AstWorkerImplGroup	* astworderimpl_group;

public:
	AstPrivate(QObject*parent):QObject(parent)
	{
		astdll_group = new AstDllGroup(this);
		astscene_group = new AstSceneGroup(this);

		// 初始化常量
		variable_group_create(&constant_group);

		// 初始化外部函数
		struct function_group_s*g = NULL;
		int r = function_group_create(&g);
		function_group = g;

		openscene_function = new OpenSceneFunction(this);
		less_function = new LessFunction(this);
		greater_function = new GreaterFunction(this);
		plus_function = new PlusFunction(this);
	}
	~AstPrivate()
	{
		if (function_group)
			function_group_free(function_group);
		if (constant_group)
			variable_group_free(constant_group);

	}
	void init()
	{
		appendFunction((AstFunction*)openscene_function);
		appendFunction((AstFunction*)less_function);
		appendFunction((AstFunction*)greater_function);
		appendFunction((AstFunction*)plus_function);

		// 初始化常量
		initLocalConstants();
	}

	void initLocalConstants()
	{
		variant_s var;

		// 
		INIT_VARIANTOBJ(var);
		SET_VARIANTOBJ_TO_PTR(var, nullptr, VALUETYPE_PTR);
		variable_group_append(constant_group, variable_create_ex("NULLPTR", var));
		//
		INIT_VARIANTOBJ(var);
		SET_VARIANTOBJ_TO_INT(var, 1);
		variable_group_append(constant_group, variable_create_ex("TRUE", var));
		////
		INIT_VARIANTOBJ(var);
		SET_VARIANTOBJ_TO_INT(var, 0);
		variable_group_append(constant_group, variable_create_ex("FALSE", var));
		
		INIT_VARIANTOBJ(var);
		variant_init_astring(&var, "bbenh.1.0.0.1");
		variable_group_append(constant_group, variable_create_ex("VERSION", var));

	}

	void appendFunction(AstFunction*f, const QString&name = "")
	{
		if (f) {
			struct function_s*fp = function_create_ex(name.isEmpty() ? f->name().toStdString().c_str() : name.toStdString().c_str()
				, (AST_FUNCTION)f->function()
				, f->outputTypes()
				, f->outputCount()
				, f->inputTypes()
				, f->inputCount(), (void*)parent()/*env*/);
			function_group_append(function_group, fp);
		}
	}
	void appendConstants(QVector<AstConstant>*arr)
	{
		if (!arr)
			return;

		variant_s var;

		for (auto a : *arr) {
			struct variable_s*v = variable_group_query(constant_group, a.name.toStdString().c_str());
			if (v)
				continue;

			variant_reset(&var);

			bool ok = true;

			switch (a.value.type()) {
			case QVariant::Double:
				SET_VARIANTOBJ_TO_DOUBLE(var, a.value.toDouble());
				break;
			case QVariant::Int:
				SET_VARIANTOBJ_TO_INT(var, a.value.toInt());
				break;
			case QVariant::String:
				variant_init_astring(&var, a.value.toString().toStdString().c_str());
				break;
			default:
				if (a.value.isNull() || !a.value.isValid()) {
					SET_VARIANTOBJ_TO_PTR(var, nullptr, VALUETYPE_PTR);
				}
				else {
					ok = false;
				}
				break;

			}
			if (ok) {
				v = variable_create_ex(a.name.toStdString().c_str(), var);
				variable_group_append(constant_group, v);
			}
		}
	}

	int loadDll(const QString&filename)
	{
		AstDll*dll = astdll_group->load(filename);

		AstStruct *as = dll->astStruct();

		// 加载常量
		appendConstants(as->constants());

		// 加载函数
		QStringList lst = as->functionNames().split(",");
		for (auto a : lst) {
			AstFunction*f = as->getFunction(a);
			appendFunction(f, a);
		}

		return 0;
	}
	int loadPath(const QString&path)
	{
		QDir dir(path);
		QFileInfoList list = dir.entryInfoList({ "*.dll" }, QDir::Files | QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);

		int r = 0;
		for each(auto a in list) {
			if (a.isDir()) {
				r += loadPath(a.absoluteFilePath());
			}
			else {
				loadDll(a.absoluteFilePath());
				r++;
			}
		}
		return r;
	}
};
Ast::Ast(QObject*parent):QObject(parent), private_ptr(new AstPrivate(this))
{
}
int Ast::init(const QString&path)
{
	private_ptr->init();

	int r = private_ptr->loadPath(path);
	//

	return 0;
}

QString Ast::sceneNames(int scene_type)
{
	return private_ptr->astdll_group->sceneNames(scene_type);
}
AstScene* Ast::queryScene(int scene_type, const QString&label)
{
	return private_ptr->astscene_group->queryScene(scene_type, label);
}
AstScene* Ast::createScene(const QString&scene_name, int scene_type, const QString&label)
{
	AstStruct * as = private_ptr->astdll_group->astStruct(scene_name, scene_type);
	if (!as)
		return nullptr;

	AstStructContext ctx;
	ctx.function_group = (void*)private_ptr->function_group;
	ctx.constant_group = (void*)private_ptr->constant_group;
	AstScene*sc = as->createScene(scene_name, scene_type, &ctx);

	sc->setLabel(label);

	private_ptr->astscene_group->addScene(sc);

	return sc;
}
void Ast::closeScene(AstScene*sc)
{
	// 移除占位
	private_ptr->astscene_group->removeScene(sc);

	// 实际删除
	AstStruct * as = private_ptr->astdll_group->astStruct(sc->typeName(), sc->type());
	if (!as)
		return;

	as->freeScene(sc);
}

void Ast::closeScene(int scene_type, const QString&label)
{
	AstScene*sc = private_ptr->astscene_group->queryScene(scene_type, label);
	if (sc)
		closeScene(sc);
}

//AstWorker* Ast::createWorker(const QString&name)
//{
//	return nullptr;// (AstWorker*)private_ptr->astworderimpl_group->createAstWorker();
//}
//
//void Ast::closeWorker(AstWorker*_)
//{
//	//private_ptr->astworderimpl_group->closeAstWorker(_);
//}
